package com.dly.blog.provider;

import com.dly.blog.exception.AuthExceptionEnum;
import com.dly.blog.token.AccountToken;
import com.dly.blog.token.EmailToken;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsChecker;
import org.springframework.security.crypto.password.PasswordEncoder;

@Slf4j
public abstract class BaseAuthenticationProvider {

    @Resource
    private PasswordEncoder passwordEncoder;

    private volatile String userNotFoundEncodedPassword;

    private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";


    protected UserDetailsChecker preAuthenticationChecks = new DefaultPreAuthenticationChecks();

    protected UserDetailsChecker postAuthenticationChecks = new DefaultPostAuthenticationChecks();

    /**
     * 校验密码的方法
     */
    protected void additionalAuthenticationChecks(UserDetails userDetails, Authentication authentication) throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            log.debug("密码不存在");
            throw AuthExceptionEnum.PASSWORD_ERROR.getAuthException();
        }
        String presentedPassword = authentication.getCredentials().toString();
        if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
            log.debug("密码错误");
            throw AuthExceptionEnum.PASSWORD_ERROR.getAuthException();
        }
    }

    protected void prepareTimingAttackProtection() {
        if (this.userNotFoundEncodedPassword == null) {
            this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD);
        }
    }

    protected void mitigateAgainstTimingAttack(Authentication authentication) {
        if (authentication.getCredentials() != null) {
            String presentedPassword = authentication.getCredentials().toString();
            this.passwordEncoder.matches(presentedPassword, this.userNotFoundEncodedPassword);
        }
    }

    private static class DefaultPreAuthenticationChecks implements UserDetailsChecker {

        @Override
        public void check(UserDetails user) {
            if (!user.isAccountNonLocked()) {
                log.debug("Failed to authenticate since user account is locked");
                throw new LockedException("AbstractUserDetailsAuthenticationProvider.locked User account is locked");
            }
            if (!user.isEnabled()) {
                throw new DisabledException("AbstractUserDetailsAuthenticationProvider.disabled  User is disabled");
            }
            if (!user.isAccountNonExpired()) {
                throw new AccountExpiredException("AbstractUserDetailsAuthenticationProvider.expired User account has expired");
            }
        }

    }

    private static class DefaultPostAuthenticationChecks implements UserDetailsChecker {
        @Override
        public void check(UserDetails user) {
            if (!user.isCredentialsNonExpired()) {
                log.debug("Failed to authenticate since user account credentials have expired");
                throw new CredentialsExpiredException("AbstractUserDetailsAuthenticationProvider.credentialsExpired User credentials have expired");
            }
        }

    }

    protected String determineUsername(Authentication authentication) {
        return (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
    }
}
